/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package xxx.smartcardio;

import java.nio.ByteBuffer;

/**
 * Just some tools for converting between hexadecimal strings and bytes.
 * @author Michael
 */
public class Tools {
    
    /*
     * Hexadecimal digits used for simple conversion 
     */
    private final static char[] hexdigits=new char[]
    {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 
     'A', 'B', 'C', 'D', 'E', 'F',
     'a', 'b', 'c', 'd', 'e', 'f'};
    private final static String hexdigitsS="0123456789ABCDEFabcdef";
 
    /**
     * Convert a value in range 0..15 to hexadecimal character
     * @param val value to convert (0..15)
     * @return corresponding hexadecimal char
     * @throws Exception 
     */
    public static char hexDigit(int val) throws Exception
    {
        if (val<0 || val>15) throw new Exception("Value out of range.");
        return hexdigits[val];
    }
    
    /**
     * Convert a hexadecimal character to integer value
     * @param c character (0..9, A..F, a..f)
     * @return corresponding value 0..15
     * @throws Exception if not a valid hexadecimal character
     */
    public static int hexValue(char c) throws Exception
    {
        int val=hexdigitsS.indexOf(c);
        if (val<0) throw new Exception("Value out of range.");
        if (val>15) val-=6;
        return val;
    }
    
    /**
     * Convert array of bytes to hexadecimal dump
     * @param bytes bytes to convert
     * @param withWhitespace if true a space is added in between two bytes
     * @return hexadecimal dump string
     */
    public static String hexString(byte[] bytes, boolean withWhitespace)
    {
        StringBuilder sb=new StringBuilder();
        for (byte b : bytes)
        {
            int val=b;
            val&=0x00FF;
            try {
                sb.append(hexDigit(val>>4));
                sb.append(hexDigit(val & 0x0F));
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }
            if (withWhitespace) sb.append(' ');
        }
        if (withWhitespace && sb.length()>0)
        {
            sb.setLength(sb.length()-1);
        }
        return sb.toString();
    }
    
    private static final int MinusByteOr=-1 & ~0x0FF;
    /**
     * Convert integer value in range 0..255 to byte value. While byte is signed
     * the value is binary identical to low 8 bits of input value.
     * @param val value (0..255) to convert
     * @return byte value (-128..127)
     */
    public static byte intToUByte(int val)
    {
        if (val>=0x080) val|=MinusByteOr;
        return (byte) val;
    }
    
    /**
     * Convert string with hexadecimal dump to byte array. The string may contain
     * whitespaces in between bytes or not.
     * @param data String to convert, i.e. "A0 08 67" or "A00867"
     * @return byte array with corresponding content
     * @throws Exception 
     */
    public static byte[] hexBytes(String data) throws Exception
    {
        if (null==data) throw new NullPointerException();
        ByteBuffer buffer=ByteBuffer.allocate(data.length()/2+2);
        int nibble=0;
        int val=0;
        for (char c: data.toCharArray())
        {
            if (Character.isWhitespace(c))
            {
                if (nibble>0) buffer.put(intToUByte(val));
                nibble=val=0;
            }
            else
            {
                int digit=hexValue(c);
                val<<=4;
                val+=digit;
                nibble++;
                if (nibble>=2)
                {
                    buffer.put(intToUByte(val));
                    nibble=val=0;
                }
            }
        }
        if (nibble>0) buffer.put(intToUByte(val));
        byte[] ret=new byte[buffer.position()];
        buffer.flip();
        buffer.get(ret);
        return ret;
    }
}
